// MyMoniker.cpp
#define _WIN32_DCOM
#include <windows.h>
#include <iostream.h>
#include <stdio.h>
#include "registry.h"

const CLSID CLSID_MarvelousMoniker = {0x10000022,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}};

long g_cComponents = 0;
long g_cServerLocks = 0;

class CMarvyMoniker : public IMoniker, public IClassActivator
{
	friend class CClassObject;
public:
	// IUnknown
	ULONG __stdcall AddRef();
	ULONG __stdcall Release();
	HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);

	// IPersist
	HRESULT __stdcall GetClassID(CLSID* pClassID);

	// IPersistStream
	HRESULT __stdcall IsDirty();
	HRESULT __stdcall Load(IStream* pStm);
	HRESULT __stdcall Save(IStream* pStm, BOOL fClearDirty);
	HRESULT __stdcall GetSizeMax(ULARGE_INTEGER *pcbSize);

	// IMoniker
	HRESULT __stdcall BindToObject(IBindCtx *pbc, IMoniker *pmkToLeft, REFIID riidResult, void **ppvResult);
	HRESULT __stdcall BindToStorage(IBindCtx *pbc, IMoniker *pmkToLeft, REFIID riid, void **ppvObj);
	HRESULT __stdcall Reduce(IBindCtx *pbc, DWORD dwReduceHowFar, IMoniker **ppmkToLeft, IMoniker **ppmkReduced);
	HRESULT __stdcall ComposeWith(IMoniker *pmkRight, BOOL fOnlyIfNotGeneric, IMoniker **ppmkComposite);
	HRESULT __stdcall Enum(BOOL fForward, IEnumMoniker **ppenumMoniker);
	HRESULT __stdcall IsEqual(IMoniker *pmkOtherMoniker);
	HRESULT __stdcall Hash(DWORD *pdwHash);
	HRESULT __stdcall IsRunning(IBindCtx *pbc, IMoniker *pmkToLeft, IMoniker *pmkNewlyRunning);
	HRESULT __stdcall GetTimeOfLastChange(IBindCtx *pbc, IMoniker *pmkToLeft, FILETIME *pFileTime);
	HRESULT __stdcall Inverse(IMoniker **ppmk);
	HRESULT __stdcall CommonPrefixWith(IMoniker *pmkOther, IMoniker **ppmkPrefix);
	HRESULT __stdcall RelativePathTo(IMoniker *pmkOther, IMoniker **ppmkRelPath);
	HRESULT __stdcall GetDisplayName(IBindCtx *pbc, IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName);
	HRESULT __stdcall ParseDisplayName(IBindCtx *pbc, IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut);
	HRESULT __stdcall IsSystemMoniker(DWORD *pdwMksys);

	// IClassActivator
	HRESULT __stdcall GetClassObject(REFCLSID pClassID, DWORD dwClsContext, LCID locale, REFIID riid, void** ppv);

	CMarvyMoniker();
	CMarvyMoniker(REFCLSID clsid, COSERVERINFO* pCSI);
	~CMarvyMoniker();

private:
	ULONG m_cRef;
	wchar_t m_hostname[255];
	CLSID m_clsid;
	COSERVERINFO m_CoServerInfo;
};

CMarvyMoniker::CMarvyMoniker(REFCLSID clsid, COSERVERINFO* pCSI)
{
	CMarvyMoniker();
	m_clsid = clsid;
	m_CoServerInfo.pwszName = wcscpy(m_hostname, pCSI->pwszName);
}

CMarvyMoniker::CMarvyMoniker() : m_cRef(1)
{
	g_cComponents++;
	m_CoServerInfo.dwReserved1 = 0;
	m_CoServerInfo.pwszName = 0;
	m_CoServerInfo.pAuthInfo = 0;
	m_CoServerInfo.dwReserved2 = 0;
}

CMarvyMoniker::~CMarvyMoniker()
{
	g_cComponents--;
}

HRESULT CMarvyMoniker::GetClassObject(REFCLSID pClassID, DWORD dwClsContext, LCID locale, REFIID riid, void** ppv)
{
	wprintf(L"IClassActivator::GetClassObject connecting to %s\n", m_CoServerInfo.pwszName);

	HRESULT hr = CoGetClassObject(pClassID, CLSCTX_SERVER, &m_CoServerInfo, riid, ppv);
	if(FAILED(hr))
		printf("CoCreateInstance failed %0x\n", hr);
	return hr;
}

HRESULT CMarvyMoniker::GetClassID(CLSID* pClassID)
{
	*pClassID = CLSID_MarvelousMoniker;
	return S_OK;
}

HRESULT CMarvyMoniker::IsDirty()
{
	return S_FALSE;
}

HRESULT CMarvyMoniker::Load(IStream* pStm)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::Save(IStream* pStm, BOOL fClearDirty)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::BindToObject(IBindCtx *pbc, IMoniker *pmkToLeft, REFIID riidResult, void **ppvResult)
{
	// This catches the recursive call by the class moniker
	if(riidResult == IID_IClassActivator)
	{
		*ppvResult = (IClassActivator*)this;
		return S_OK;
	}

	// An AddRef a day keeps the doctor away
	AddRef();

	IMoniker* pClassMoniker;
	HRESULT hr = CreateClassMoniker(m_clsid, &pClassMoniker);
	if(FAILED(hr))
		return hr;

	// Bind the class moniker
	hr = pClassMoniker->BindToObject(pbc, (IMoniker*)this, riidResult, ppvResult);
	pClassMoniker->Release();
	return hr;
}

HRESULT CMarvyMoniker::BindToStorage(IBindCtx *pbc, IMoniker *pmkToLeft, REFIID riid, void **ppvObj)
{
	return MK_E_NOSTORAGE;
}

HRESULT CMarvyMoniker::Reduce(IBindCtx *pbc, DWORD dwReduceHowFar, IMoniker **ppmkToLeft, IMoniker **ppmkReduced)
{
	*ppmkReduced = (IMoniker*)this;
	return MK_S_REDUCED_TO_SELF;
}

HRESULT CMarvyMoniker::ComposeWith(IMoniker *pmkRight, BOOL fOnlyIfNotGeneric, IMoniker **ppmkComposite)
{
	if(fOnlyIfNotGeneric)
	{
		*ppmkComposite = NULL;
		return MK_E_NEEDGENERIC;
	}
	return CreateGenericComposite((IMoniker*)this, pmkRight, ppmkComposite);
}

HRESULT CMarvyMoniker::Enum(BOOL fForward, IEnumMoniker **ppenumMoniker)
{
	*ppenumMoniker = NULL;
	return S_OK;
}

HRESULT CMarvyMoniker::IsEqual(IMoniker *pmkOtherMoniker)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::Hash(DWORD *pdwHash)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::IsRunning(IBindCtx *pbc, IMoniker *pmkToLeft, IMoniker *pmkNewlyRunning)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::GetTimeOfLastChange(IBindCtx *pbc, IMoniker *pmkToLeft, FILETIME *pFileTime)
{
	return MK_E_UNAVAILABLE;
}

HRESULT CMarvyMoniker::Inverse(IMoniker **ppmk)
{
	return CreateAntiMoniker(ppmk);
}

HRESULT CMarvyMoniker::CommonPrefixWith(IMoniker *pmkOther, IMoniker **ppmkPrefix)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::RelativePathTo(IMoniker *pmkOther, IMoniker **ppmkRelPath)
{
	return MonikerRelativePathTo((IMoniker*)this, pmkOther, ppmkRelPath, TRUE);
}

HRESULT CMarvyMoniker::GetDisplayName(IBindCtx *pbc, IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
{
	*ppszDisplayName = (wchar_t*)CoTaskMemAlloc(512);
	wchar_t ppsz[39];
	StringFromGUID2(m_clsid, ppsz, 39);
	ppsz[37] = 0;
	swprintf(*ppszDisplayName, L"host:%s!clsid:%s", m_hostname, wcstok(ppsz, L"{"));
	return S_OK;
}

HRESULT CMarvyMoniker::ParseDisplayName(IBindCtx *pbc, IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
{
	return E_NOTIMPL;
}

HRESULT CMarvyMoniker::IsSystemMoniker(DWORD *pdwMksys)
{
	*pdwMksys = MKSYS_NONE;
	return S_OK;
}

ULONG CMarvyMoniker::AddRef()
{
	cout << "Moniker::AddRef() m_cRef = " << m_cRef + 1 << endl;
	return ++m_cRef;
}

ULONG CMarvyMoniker::Release()
{
	cout << "Moniker::Release() m_cRef = " << m_cRef - 1 << endl;
	if(--m_cRef != 0)
		return m_cRef;
	delete this;
	return 0;
}

HRESULT CMarvyMoniker::QueryInterface(REFIID riid, void** ppv)
{
	if(riid == IID_IUnknown)
	{
		cout << "Moniker::QueryInterface() for IUnknown" << endl;
		*ppv = reinterpret_cast<IUnknown*>(this);
	}
	else if(riid == IID_IMoniker)
	{
		cout << "Moniker::QueryInterface() for IMoniker" << endl;
		*ppv = (IMoniker*)this;
	}
	else 
	{
		*ppv = NULL;
		return E_NOINTERFACE;
	}
	AddRef();
	return S_OK;
}

class CClassObject : public IParseDisplayName
{
public:
	// IUnknown
	ULONG __stdcall AddRef();
	ULONG __stdcall Release();
	HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);

	// IParseDisplayName
	HRESULT __stdcall ParseDisplayName(IBindCtx *pbc, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut);

	CClassObject() : m_cRef(1) { }
	~CClassObject() { }

private:
	ULONG m_cRef;
};

ULONG CClassObject::AddRef()
{
	return ++m_cRef;
}

ULONG CClassObject::Release()
{
	if(--m_cRef != 0)
		return m_cRef;
	delete this;
	return 0;
}

HRESULT CClassObject::QueryInterface(REFIID riid, void** ppv)
{
	if(riid == IID_IUnknown)
		*ppv = (IUnknown*)this;
	else if(riid == IID_IParseDisplayName)
	{
		cout << "CClassObject::QueryInterface() for IParseDisplayName" << endl;
		*ppv = (IParseDisplayName*)this;
	}
	else
	{
		*ppv = NULL;
		return E_NOINTERFACE;
	}
	AddRef();
	return S_OK;
}

HRESULT CClassObject::ParseDisplayName(IBindCtx *pbc, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
{
	// Instantiate the moniker
	CMarvyMoniker* pCMarvyMoniker = new CMarvyMoniker();

	// Parse and check display name
	// It must have the following format:
	// host:hostname!clsid:????????-????-????-????-????????????
	if(_wcsicmp(wcstok(pszDisplayName, L":"), L"host") == 0)
	{
		pCMarvyMoniker->m_CoServerInfo.pwszName = wcscpy(pCMarvyMoniker->m_hostname, wcstok(NULL, L"!"));
		if(_wcsicmp(wcstok(NULL, L":"), L"clsid") == 0)
		{
			wchar_t clsid_with_braces[39] = L"{";
			wcscat(wcscat(clsid_with_braces, wcstok(NULL, L"!")), L"}");
			CLSIDFromString(clsid_with_braces, &pCMarvyMoniker->m_clsid);
		}
	}

	// Get IMoniker* to return to caller
	pCMarvyMoniker->QueryInterface(IID_IMoniker, (void**)ppmkOut);
	pCMarvyMoniker->Release();

	// Indicate that we have digested the entire display name.
	*pchEaten = (ULONG)wcslen(pszDisplayName);
	return S_OK;
}

HRESULT __stdcall DllCanUnloadNow()
{
	cout << "Moniker: DllCanUnloadNow() " << (g_cServerLocks == 0 && g_cComponents == 0 ? "Yes" : "No") << endl;
	if(g_cServerLocks == 0 && g_cComponents == 0)
		return S_OK;
	else
		return S_FALSE;
}

HRESULT __stdcall DllGetClassObject(REFCLSID clsid, REFIID riid, void** ppv)
{
	cout << "Moniker: DllGetClassObject" << endl;
	
	if(clsid != CLSID_MarvelousMoniker)
		return CLASS_E_CLASSNOTAVAILABLE;

	CClassObject* pClassObject = new CClassObject;
	if(pClassObject == NULL)
		return E_OUTOFMEMORY;

	// QueryInterface probably for IClassFactory
	HRESULT hr = pClassObject->QueryInterface(riid, ppv);
	pClassObject->Release();
	return hr;
}

HRESULT __stdcall DllRegisterServer()
{
	// The progid "Host" must be registered in order for the moniker to work
	return RegisterServer("moniker.dll", CLSID_MarvelousMoniker, "Marvelous Moniker", "Host", "Host", NULL);
}

HRESULT __stdcall DllUnregisterServer()
{
	return UnregisterServer(CLSID_MarvelousMoniker, "Host", "Host");
}

HRESULT __stdcall CreateMarvelousMoniker(REFCLSID clsid, COSERVERINFO* pCSI, IMoniker** ppMoniker)
{
	CMarvyMoniker *pCMarvyMoniker = new CMarvyMoniker(clsid, pCSI);
	if(pCMarvyMoniker == NULL)
		return E_OUTOFMEMORY;

	HRESULT hr = pCMarvyMoniker->QueryInterface(IID_IMoniker, (void**)ppMoniker);
	pCMarvyMoniker->Release();
	return hr;
}